<?php
	class GP_zip
	{
		var $datasec, $ctrl_dir = array();
		var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
		var $old_offset = 0; 
		var $dirs = array(".");
		
		static $_instance;
		
		// singleton
		public function &getInstance() 
		{
			if (!self::$_instance instanceof self)
				self::$_instance = new self();
			return self::$_instance;
		}
		
		function __construct() {}

		function get_List($nom_zip)
		{
			$zip = @fopen($nom_zip, 'rb');
			if(!$zip) 
				return(0);
			$centd = $this -> ReadCentralDir($zip, $nom_zip);

			@rewind($zip);
			@fseek($zip, $centd['offset']);

			for ($i = 0; $i < $centd['entries'] ; $i++)
			{
				$entetes = $this -> ReadCentralFileHeaders($zip);
				$entetes['index'] = $i;
				$info['filename'] = $entetes['filename'];
				$info['stored_filename'] = $entetes['stored_filename'];
				$info['size'] = $entetes['size'];
				$info['compressed_size'] = $entetes['compressed_size'];
				$info['crc'] = strtoupper(dechex($entetes['crc']));
				$info['mtime'] = $entetes['mtime']; 
				$info['comment'] = $entetes['comment'];
				$info['folder'] = ($entetes['external'] == 0x41FF0010 ||$entetes['external'] == 16) ? 1 : 0;
				$info['index'] = $entetes['index'];
				$info['status'] = $entetes['status'];
				$ret[] = $info; 
				unset($entetes);
			}
			return $ret;
		}

		function Add($fichiers, $compact)
		{
			if(!is_array($fichiers[0])) 
				$fichiers = array($fichiers);

			for($i = 0 ; $fichiers[$i] ; $i++)
			{
				$fn = $fichiers[$i];
				if(!in_Array(dirname($fn[0]), $this -> dirs))
					$this -> add_Dir(dirname($fn[0]));
				if(basename($fn[0]))
					$ret[basename($fn[0])] = $this -> add_File($fn[1], $fn[0] ,$compact);
			}
			return $ret;
		}


		function Compress($folder, $basedir = null, $parent = null)
		{
			$full_path = $basedir . "/" . $parent . $folder;
			$zip_path = $parent.$folder;
			if ($zip_path) 
			{
				$zip_path .= "/";
				$this -> add_dir($zip_path);
			}
			$dir = new DirectoryIterator($full_path);
			foreach($dir as $file) 
			{
				if(!$file -> isDot()) 
				{
					$filename = $file -> getFilename();
					if($file -> isDir()) 
						$this -> Compress($filename, $basedir, $zip_path);
					else 
					{
						$content = join('', file($full_path.'/'.$filename));
						$this -> add_File($content, $zip_path.$filename);
					}
				}
			}
		}

		function get_file()
		{
			$data = implode('', $this -> datasec);
			$ctrldir = implode('', $this -> ctrl_dir);

			return $data . $ctrldir . $this -> eof_ctrl_dir . pack('v', sizeof($this -> ctrl_dir)).pack('v', sizeof($this -> ctrl_dir)). pack('V', strlen($ctrldir)) . pack('V', strlen($data)) . "\x00\x00";
		}

		function add_dir($nom)
		{
			$nom = str_replace("\\", "/", $nom);
			$fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";

			$fr .= pack("V", 0) . pack("V", 0) . pack("V", 0) . pack("v", strlen($nom));
			$fr .= pack("v", 0 ) . $nom . pack("V", 0) . pack("V", 0) . pack("V", 0);
			$this -> datasec[] = $fr;

			$new_offset = strlen(implode("", $this -> datasec));

			$cdrec = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";
			$cdrec .= pack("V", 0) . pack("V", 0) . pack("V", 0) . pack("v", strlen($nom));
			$cdrec .= pack("v", 0 ) . pack("v", 0 ) . pack("v", 0 ) . pack("v", 0 );
			$ext = "\xff\xff\xff\xff";
			$cdrec .= pack("V", 16 ) . pack("V", $this -> old_offset) . $nom;

			$this -> ctrl_dir[] = $cdrec;
			$this -> old_offset = $new_offset;
			$this -> dirs[] = $nom;
		}

		function add_File($data, $nom, $compact = 1)
		{
			$nom     = str_replace('\\', '/', $nom);
			$dtime    = dechex($this -> DosTime());

			$hexdtime = '\x' . $dtime[6] . $dtime[7] . '\x' . $dtime[4] . $dtime[5]	 . '\x' . $dtime[2] . $dtime[3] . '\x' . $dtime[0] . $dtime[1];
			eval('$hexdtime = "' . $hexdtime . '";');

			if($compact)
				$fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00" . $hexdtime;
			else 
				$fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" . $hexdtime;
			$unc_len = strlen($data); 
			$crc = crc32($data);

			if($compact)
			{
				$zdata = gzcompress($data); 
				$c_len = strlen($zdata);
				$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
			}
			else
				$zdata = $data;
				
			$c_len = strlen($zdata);
			$fr .= pack('V', $crc) . pack('V', $c_len).pack('V', $unc_len);
			$fr .= pack('v', strlen($nom)) . pack('v', 0) . $nom . $zdata;

			$fr .= pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);

			$this -> datasec[] = $fr;
			$new_offset        = strlen(implode('', $this -> datasec));
			if($compact)
				$cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00";
			else 
				$cdrec = "\x50\x4b\x01\x02\x14\x00\x0a\x00\x00\x00\x00\x00";
			$cdrec .= $hexdtime . pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);
			$cdrec .= pack('v', strlen($nom) ) . pack('v', 0 ) . pack('v', 0 );
			$cdrec .= pack('v', 0 ) . pack('v', 0 ) . pack('V', 32 );
			$cdrec .= pack('V', $this -> old_offset );

			$this -> old_offset = $new_offset;
			$cdrec .= $nom;
			$this -> ctrl_dir[] = $cdrec;
			return true;
		}

		function DosTime() 
		{
			$timearray = getdate();
			if ($timearray['year'] < 1980) 
			{
				$timearray['year'] = 1980; $timearray['mon'] = 1;
				$timearray['mday'] = 1; $timearray['hours'] = 0;
				$timearray['minutes'] = 0; $timearray['seconds'] = 0;
			}
			return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) |	($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
		}

		function Extract ($zn, $to, $index = array(-1))
		{
			if(!@is_dir($to)) 
				$this -> mkdir($to);
			$ok = 0; 
				$zip = @fopen($zn,'rb');
			if(!$zip) 
				return (-1);
			$cdir = $this -> ReadCentralDir($zip, $zn);
			$pos_entry = $cdir['offset'];

			if(!is_array($index))
				$index = array($index);
			for($i = 0 ; isset($index[$i]) ; $i++)
			{
				if(intval($index[$i]) <> $index[$i] || $index[$i] > $cdir['entries'])
					return (-1);
			}

			for ($i = 0 ; $i < $cdir['entries'] ; $i++)
			{
				@fseek($zip, $pos_entry);
				$entetes = $this -> ReadCentralFileHeaders($zip);
				$entetes['index'] = $i; $pos_entry = ftell($zip);
				@rewind($zip); fseek($zip, $entetes['offset']);
				if(in_array("-1",$index)||in_array($i,$index))
				$stat[$entetes['filename']]=$this -> ExtractFile($entetes, $to, $zip);

			}
			fclose($zip);
			return $stat;
		}

		function ReadFileHeader ($zip, $entetes)
		{
			$binary_data = fread($zip, 30);
			$data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);

			$entetes['filename'] = fread($zip, $data['filename_len']);
			if ($data['extra_len'] <> 0)
				$entetes['extra'] = fread($zip, $data['extra_len']);
			else 
				$entetes['extra'] = '';

			$entetes['compression'] = $data['compression'];
			
			foreach (array('size', 'compressed_size','crc') as $hd) 
			{
				if ($data[$hd] <> 0) 
					$entetes[$hd] = $data[$hd];
			}
			
			$entetes['flag'] = $data['flag'];
			$entetes['mdate'] = $data['mdate'];$entetes['mtime'] = $data['mtime'];

			if ($entetes['mdate'] && $entetes['mtime'])
			{
				$hour = ($entetes['mtime'] & 0xF800) >> 11;
				$minute =($entetes['mtime'] & 0x07E0) >> 5;
				$seconde = ($entetes['mtime'] & 0x001F) * 2;
				$year = (($entetes['mdate'] & 0xFE00) >> 9) + 1980;
				$month = ($entetes['mdate'] & 0x01E0) >> 5;
				$day = $entetes['mdate'] & 0x001F;
				$entetes['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
			}
			else
				$entetes['mtime'] = time();

			$entetes['stored_filename'] = $entetes['filename'];
			$entetes['status'] = "ok";
			return $entetes;
		}

		function ReadCentralFileHeaders($zip)
		{
			$binary_data = fread($zip, 46);
			$entetes = unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data);

			if ($entetes['filename_len'] <> 0)
				$entetes['filename'] = fread($zip, $entetes['filename_len']);
			else 
				$entetes['filename'] = '';

			if ($entetes['extra_len'] <> 0)
				$entetes['extra'] = fread($zip, $entetes['extra_len']);
			else 
				$entetes['extra'] = '';

			if ($entetes['comment_len'] <> 0)
				$entetes['comment'] = fread($zip, $entetes['comment_len']);
			else 
				$entetes['comment'] = '';

			if ($entetes['mdate'] && $entetes['mtime'])
			{
				$hour = ($entetes['mtime'] & 0xF800) >> 11;
				$minute = ($entetes['mtime'] & 0x07E0) >> 5;
				$seconde = ($entetes['mtime'] & 0x001F) * 2;
				$year = (($entetes['mdate'] & 0xFE00) >> 9) + 1980;
				$month = ($entetes['mdate'] & 0x01E0) >> 5;
				$day = $entetes['mdate'] & 0x001F;
				$entetes['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
			} 
			else
				$entetes['mtime'] = time();

			$entetes['stored_filename'] = $entetes['filename'];
			$entetes['status'] = 'ok';
			
			if (substr($entetes['filename'], -1) == '/')
				$entetes['external'] = 0x41FF0010;
				
			return $entetes;
	 }

		function ReadCentralDir($zip, $nom_zip)
		{
			$size = filesize($nom_zip);
			if ($size < 277) 
				$maximum_size = $size;
			else 
				$maximum_size = 277;

			@fseek($zip, $size - $maximum_size);
			$pos = ftell($zip); 
			$bytes = 0x00000000;

			while ($pos < $size)
			{
				$byte = @fread($zip, 1);
				$bytes = (($bytes << 8) & 0xFFFFFFFF) | ord($byte);
				if ($bytes == 0x504b0506)
				{ 
					$pos++; 
					break; 
				} 
				$pos++;
			}

			$data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size',
			fread($zip, 18));

			if ($data['comment_size'] <> 0)
				$centd['comment'] = fread($zip, $data['comment_size']);
			else $centd['comment'] = ''; 
				$centd['entries'] = $data['entries'];
				
			$centd['disk_entries'] = $data['disk_entries'];
			$centd['offset'] = $data['offset'];$centd['disk_start'] = $data['disk_start'];
			$centd['size'] = $data['size'];  $centd['disk'] = $data['disk'];
			
			return $centd;
		}

		function ExtractFile($entetes, $to, $zip)
		{
			$entetes = $this -> readfileheader($zip, $entetes);

			if(substr($to, -1) <> "/") 
				$to .= "/";
			if(substr($entetes['filename'], -1) == "/")
			{
				$this -> mkdir($to.$entetes['filename']); 
				return +2;
			}


			if (!$this -> mkdir($to . dirname($entetes['filename']))) 
				return (-1);

			if (!array_key_exists("external", $entetes) || (!($entetes['external'] == 0x41FF0010)&&!($entetes['external']==16)))
			{
				if ($entetes['compression'] == 0)
				{
					$fp = @fopen($to . $entetes['filename'], 'wb');
					if(!$fp) 
						return(-1);
					$size = $entetes['compressed_size'];

					while ($size <> 0)
					{
						$read_size = ($size < 2048 ? $size : 2048);
						$buffer = fread($zip, $read_size);
						$binary_data = pack('a' . $read_size, $buffer);
						@fwrite($fp, $binary_data, $read_size);
						$size -= $read_size;
					}
					fclose($fp);
					touch($to . $entetes['filename'], $entetes['mtime']);

				}
				else
				{
					if (!is_dir(dirname($to . $entetes['filename']))) 
						$this -> mkdir(dirname($to . $entetes['filename']));
					$fp = fopen($to . $entetes['filename'] . '.gz', 'wb');
					if(!$fp) 
						return -1;
					$binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($entetes['compression']),	chr(0x00), time(), chr(0x00), chr(3));

					fwrite($fp, $binary_data, 10);
					$size = $entetes['compressed_size'];

					while ($size <> 0)
					{
						$read_size = ($size < 1024 ? $size : 1024);
						$buffer = fread($zip, $read_size);
						$binary_data = pack('a' . $read_size, $buffer);
						@fwrite($fp, $binary_data, $read_size);
						$size -= $read_size;
					}

					$binary_data = pack('VV', $entetes['crc'], $entetes['size']);
					fwrite($fp, $binary_data,8); 
					fclose($fp);

					$gzp = @gzopen($to . $entetes['filename'] . '.gz','rb');
					if(!$gzp)
					{
						@gzclose($gzp); @unlink($to . $entetes['filename']);
						GP_debug::erreur("Archive is compressed whereas ZLIB is not enabled.", 1);
					}
					$fp = @fopen($to . $entetes['filename'],'wb');
					if(!$fp) 
						return -1;
					$size = $entetes['size'];

					while ($size <> 0)
					{
						$read_size = ($size < 2048 ? $size : 2048);
						$buffer = gzread($gzp, $read_size);
						$binary_data = pack('a' . $read_size, $buffer);
						@fwrite($fp, $binary_data, $read_size);
						$size -= $read_size;
					}
					fclose($fp); gzclose($gzp);

					touch($to . $entetes['filename'], $entetes['mtime']);
					@unlink($to . $entetes['filename'] . '.gz');
				}
			}
			return true;
		}

		function ExtractStr($zn, $nom) 
		{
			$ok = 0;
			$zip = @fopen($zn, 'rb');
			if(!$zip) 
				return null;
			$cdir = $this -> ReadCentralDir($zip, $zn);
			$pos_entry = $cdir['offset'];

			for ($i = 0 ; $i < $cdir['entries'] ; $i++)
			{
				@fseek($zip, $pos_entry);
				$entetes = $this -> ReadCentralFileHeaders($zip);
				$entetes['index'] = $i;
				$pos_entry = ftell($zip);
				@rewind($zip);
				fseek($zip, $entetes['offset']);
				if ($nom == $entetes['stored_filename'] || $nom == $entetes['filename']) 
				{
					$str = $this -> ExtractStrFile($entetes, $zip);
					fclose($zip);
					return $str;
				}
			}
			fclose($zip);
			return null;
		}

		function ExtractStrFile($entetes, $zip) 
		{
			$hdr = $this -> readfileheader($zip);
			$binary_data = '';
			if (!($entetes['external'] == 0x41FF0010) && !($entetes['external']==16))
			{
				if ($entetes['compression'] == 0)
				{
					while ($size <> 0)
					{
						$read_size = ($size < 2048 ? $size : 2048);
						$buffer = fread($zip, $read_size);
						$binary_data .= pack('a' . $read_size, $buffer);
						$size -= $read_size;
					}
					return $binary_data;
				} 
				else 
				{
					$size = $entetes['compressed_size'];
					if ($size == 0)
						return '';

					if ($size > ($this -> _ret_bytes(ini_get('memory_limit')) / 2))
						GP_debug::erreur("Compressed file is to huge to be uncompress in memory.", 1);
					while ($size <> 0)
					{
						$read_size = ($size < 2048 ? $size : 2048);
						$buffer = fread($zip, $read_size);
						$binary_data .= pack('a' . $read_size, $buffer);
						$size -= $read_size;
					}
					$str = gzinflate($binary_data, $entetes['size']);
					if ($entetes['crc'] == crc32($str))
						return $str;
					else
						GP_debug::erreur("Crc Error", 1);
				}
			}
			return null;
		}

		function _ret_bytes($val) 
		{
		   $val = trim($val);
		   $last = $val{strlen($val) - 1};
		   switch($last) 
		   {
			   case 'k':
			   case 'K':
					return (int) $val * 1024;
					break;
			   case 'm':
			   case 'M':
					return (int) $val * 1048576;
					break;
			   default:
					return $val;
		   }
		}
		
		function __destruct() {}
	}
?>